package com.atguigu.process.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.auth.service.SysUserService;
import com.atguigu.common.result.Result;
import com.atguigu.model.process.Process;
import com.atguigu.model.process.ProcessRecord;
import com.atguigu.model.process.ProcessTemplate;
import com.atguigu.model.process.ProcessType;
import com.atguigu.model.system.SysUser;
import com.atguigu.process.mapper.ProcessMapper;
import com.atguigu.process.service.*;
import com.atguigu.security.custom.LoginUserInfoHelper;
import com.atguigu.vo.process.ApprovalVo;
import com.atguigu.vo.process.ProcessFormVo;
import com.atguigu.vo.process.ProcessQueryVo;
import com.atguigu.vo.process.ProcessVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.*;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.parameters.P;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ResourceUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

@Slf4j
@Service
public class ProcessServiceImpl extends ServiceImpl<ProcessMapper, Process>
    implements ProcessService {
    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private SysUserService userService;

    @Autowired
    private ProcessTemplateService processTemplateService;

    @Autowired
    private ProcessTypeService processTypeService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private ProcessRecordService processRecordService;

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private MessageService messageService;

    @Override
    public Result<IPage<ProcessVo>> pageQuery(Long page, Long limit,
                                              ProcessQueryVo processQueryVo) {
        IPage<ProcessVo> pageParam = new Page<>(page,limit);
        IPage<ProcessVo> pageModel = baseMapper.selectPage(pageParam,processQueryVo,null);
        return Result.ok(pageModel);
    }

    @Override
    public void deployByZip(String deployPath) {
//        InputStream inputStream = this.getClass().getClassLoader()
//                .getResourceAsStream(deployPath);
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream( "E:\\temp\\process\\" + deployPath);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);
        //部署
        Deployment deploy = repositoryService.createDeployment()
                .addZipInputStream(zipInputStream)
                .deploy();
        System.out.println(deploy.getId());
        System.out.println(deploy.getName());
    }

    @Transactional
    @Override
    public void startUp(ProcessFormVo processFormVo) {
        //1.首先根据用户id获取用户信息
        SysUser user = userService.getById(LoginUserInfoHelper.getUserId());
        //2.然后根据模板id获取模板信息
        ProcessTemplate template = processTemplateService.getById(processFormVo.getProcessTemplateId());
        //3.保存提交审批信息到业务表process
        Process process = new Process();
        process.setProcessCode(System.currentTimeMillis() + "");
        process.setUserId(user.getId());
        BeanUtils.copyProperties(processFormVo,process);
        process.setTitle(user.getName() + "发起" + template.getName());
        process.setStatus(1);
        save(process);
        //4.启动流程实例
        //三个参数：
        //  4.1流程定义的key
        //  4.2业务key    processId
        //  4.3流程参数    form表单json数据，转换map集合
        String formValues = processFormVo.getFormValues();
        JSONObject jsonObject = JSON.parseObject(formValues);
        JSONObject formData = jsonObject.getJSONObject("formData");
        //遍历formData得到内容，封装map集合
        Map<String,Object> map = new HashMap<>();
        for(Map.Entry<String,Object> entry : formData.entrySet()){
            map.put(entry.getKey(),entry.getValue());
        }
        Map<String,Object> variables = new HashMap<>();
        variables.put("data",map);
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(
                template.getProcessDefinitionKey(),process.getId().toString(),variables);
        process.setProcessInstanceId(instance.getId());
        //5.查询下一个审批人
        List<Task> tasks = getCurrentTaskList(instance.getId());
        List<String> nameList = new ArrayList<>();
        for(Task task : tasks){
            String assignName = task.getAssignee();
            SysUser sysUser = userService.getOne(new LambdaQueryWrapper<SysUser>()
                    .eq(SysUser::getUsername, assignName));
            nameList.add(sysUser.getName());
            //6.给需要审批任务的人发送消息
            messageService.pushPendingMessage(process.getId(),sysUser.getId(),task.getId());
        }
        //7.更新process中的下一个审批人的信息
        process.setDescription("等待" + StringUtils.join(nameList.toArray(),",")
                + "审批");
        updateById(process);
        //8.记录申请操作记录
        processRecordService.record(process.getId(),1,process.getDescription());
    }

    //获取某个流程实例的任务列表
    private List<Task> getCurrentTaskList(String id) {
        return taskService.createTaskQuery()
                .processInstanceId(id).list();
    }

    @Override
    public Page<ProcessVo> findPengding(Long page, Long limit) {
        // 根据当前人的ID查询
        TaskQuery query = taskService.createTaskQuery().taskAssignee(LoginUserInfoHelper.getUsername()).orderByTaskCreateTime().desc();
        List<Task> list = query.listPage((int) ((page - 1) * limit), limit.intValue());
        long totalCount = query.count();

        List<ProcessVo> processList = new ArrayList<>();
        // 根据流程的业务ID查询实体并关联
        for (Task item : list) {
            String processInstanceId = item.getProcessInstanceId();
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            if (processInstance == null) {
                continue;
            }
            // 业务key
            String businessKey = processInstance.getBusinessKey();
            if (businessKey == null) {
                continue;
            }
            Process process = this.getById(Long.parseLong(businessKey));
            ProcessVo processVo = new ProcessVo();
            BeanUtils.copyProperties(process, processVo);
            processVo.setTaskId(item.getId());
            processList.add(processVo);
        }
        Page<ProcessVo> pageVo = new Page<ProcessVo>(page, limit, totalCount);
        pageVo.setRecords(processList);
        return pageVo;
    }

    //    @Override
    public Page<ProcessVo> findPengding2(Long page, Long limit) {
        String username = LoginUserInfoHelper.getUsername();
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(username).list();
        Set<String> processInstanceIds = tasks.stream()
                .map(t -> {
                    log.info("当前任务task的业务businessKey为：{}",t.getBusinessKey());
                    return t.getProcessInstanceId();
                }).collect(Collectors.toSet());
        List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery()
                .processInstanceIds(processInstanceIds)
                .list();
        List<Long> processIds = processInstances.stream().map(pi -> {
            log.info("当前流程实例instance的业务businessKey为：{}", pi.getBusinessKey());
            return Long.parseLong(pi.getBusinessKey());
        }).collect(Collectors.toList());
        Page<Process> pageInfo = new Page<>(page,limit);
        page(pageInfo,new LambdaQueryWrapper<Process>()
                .in(Process::getId,processIds));
        List<Process> processlist = pageInfo.getRecords();
        List<Long> userIds = new ArrayList<>();
        List<Long> templateIds = new ArrayList<>();
        List<Long> typeIds = new ArrayList<>();
        processlist.forEach(p -> {
            userIds.add(p.getUserId());
            templateIds.add(p.getProcessTemplateId());
            typeIds.add(p.getProcessTypeId());
        });
        Map<Long, SysUser> userMap = userService.listByIds(userIds).stream()
                .collect(Collectors.toMap(SysUser::getId, u -> u,
                        (oldKey, newKey) -> newKey));
        Map<Long, ProcessTemplate> templateMap = processTemplateService.listByIds(templateIds)
                .stream()
                .collect(Collectors.toMap(ProcessTemplate::getId, temp -> temp,
                        (oldKey, newKey) -> newKey));
        Map<Long, ProcessType> typeMap = processTypeService.listByIds(typeIds).stream()
                .collect(Collectors.toMap(ProcessType::getId, type -> type,
                        (oldKey, newKey) -> newKey));
        List<ProcessVo> voList = processlist.stream().map(process -> {
            ProcessVo vo = new ProcessVo();
            BeanUtils.copyProperties(process, vo);
            Long userId = vo.getUserId();
            SysUser sysUser = userMap.get(userId);
            if (sysUser != null) {
                vo.setName(sysUser.getName());
            }
            Long templateId = vo.getProcessTemplateId();
            ProcessTemplate processTemplate = templateMap.get(templateId);
            if (processTemplate != null) {
                vo.setProcessTemplateName(processTemplate.getName());
                vo.setFormProps(processTemplate.getFormProps());
                vo.setFormOptions(processTemplate.getFormOptions());
            }
            Long typeId = vo.getProcessTypeId();
            ProcessType processType = typeMap.get(typeId);
            if (processType != null) {
                vo.setProcessTypeName(processType.getName());
            }
            return vo;
        }).collect(Collectors.toList());
        Page<ProcessVo> voPage = new Page<>(page,limit,pageInfo.getTotal());
        voPage.setRecords(voList);
        return voPage;
    }

    /**
     * 查看审批详情信息
     * @param processId
     * @return
     */
    @Override
    public Map<String, Object> show(Long processId) {
        //1.根据processId获取process信息
        Process process = getById(processId);
        //2.获取流程审批记录信息
        List<ProcessRecord> records = processRecordService.list(
                new LambdaQueryWrapper<ProcessRecord>()
                        .eq(ProcessRecord::getProcessId, processId));
        //3.获取流程审批模板信息
        ProcessTemplate template = processTemplateService.getById(process.getProcessTemplateId());
        //4.判断当前用户是否可以审批，有些人可以看到，但是不能审批，而且不能重复审批
        boolean isApprove = false;
        List<Task> taskList = getCurrentTaskList(process.getProcessInstanceId());
        for(Task task : taskList){
            if(task.getAssignee().equals(LoginUserInfoHelper.getUsername())){
                isApprove = true;
            }
        }
        //5.封装map集合
        Map<String, Object> map = new HashMap<>();
        map.put("process", process);
        map.put("processRecordList", records);
        map.put("processTemplate", template);
        map.put("isApprove", isApprove);
        return map;
    }

    /**
     * 审批
     * @param approvalVo
     */
    @Override
    public void approve(ApprovalVo approvalVo) {
        //1.获取任务id，根据任务id获取流程变量
        String taskId = approvalVo.getTaskId();
        Map<String, Object> variables = taskService.getVariables(taskId);
        for(Map.Entry<String,Object> entry : variables.entrySet()){
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
        }
        //2.判断审批状态
        if(approvalVo.getStatus() == 1){
            //2.1 状态值=1 审批通过
            Map<String,Object> variable = new HashMap<>();
            taskService.complete(taskId,variable);
        }else{
            //2.2 状态值= -1   审批拒绝
            endTask(taskId);
        }
        //3.记录审批相关过程信息  processRecord
        processRecordService.record(approvalVo.getProcessId(),approvalVo.getStatus(),
                approvalVo.getStatus() == 1 ? "已通过" : "驳回");
        //4.查询下一个审批人，更新流程表process中的记录
        Process process = getById(approvalVo.getProcessId());
        List<Task> taskList = getCurrentTaskList(process.getProcessInstanceId());
        if(!CollectionUtils.isEmpty(taskList)){
            List<String> assignList = new ArrayList<>();
            for(Task task : taskList){
                String username = task.getAssignee();
                SysUser user = sysUserService.getOne(new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getUsername, username));
                assignList.add(user.getName());
                //推送消息给下一个审批人
                messageService.pushPendingMessage(process.getId(), user.getId(), task.getId());
            }
            //更新process流程信息
            process.setDescription("等待" + StringUtils.join(assignList.toArray(),",")
             + "审批");
            process.setStatus(1);
        }else{
            if(approvalVo.getStatus() == 1){
                process.setStatus(2);
                process.setDescription("审批完成（通过）");
            }else{
                process.setStatus(-1);
                process.setDescription("审批完成（驳回）");
            }
        }
        //推送消息给申请人
        messageService.pushProcessedMessage(process.getId(), process.getUserId(), approvalVo.getStatus());
        updateById(process);
    }

    /**
     * 结束流程
     * @param taskId
     */
    private void endTask(String taskId) {
        //1.根据流程id获取任务对象Task
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        //2.获取流程定义模型BpmnModel
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessInstanceId());
        //3.获取结束流向节点
        List<EndEvent> endEventList = bpmnModel.getMainProcess()
                .findFlowElementsOfType(EndEvent.class);
        if(CollectionUtils.isEmpty(endEventList)){
            return;
        }
        FlowNode endFlowNode = endEventList.get(0);
        //4.获取当前流向节点
        FlowNode currentFlowNode = (FlowNode)bpmnModel.getMainProcess()
                .getFlowElement(task.getTaskDefinitionKey());
        //临时保存活动的原始方向
        List originalSequenceFlowList = new ArrayList();
        originalSequenceFlowList.add(currentFlowNode.getOutgoingFlows());
        //5.清理当前流动方向
        currentFlowNode.getOutgoingFlows().clear();
        //6.创建新流向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlow");
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        newSequenceFlow.setTargetFlowElement(endFlowNode);
        //7.当前节点指向新方向
        List newSequenceFlowList = new ArrayList();
        newSequenceFlowList.add(newSequenceFlow);
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);
        //8.完成当前任务
        taskService.complete(taskId);
    }

    /**
     * 查询当前用户已处理的审批任务
     * @param page
     * @param limit
     * @return
     */
    @Override
    public Page<ProcessVo> findProcessed(Long page, Long limit) {
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(LoginUserInfoHelper.getUsername())
                .finished().orderByTaskCreateTime().desc();
        List<HistoricTaskInstance> historicTasks = query.listPage((int)((page - 1) * limit),
                limit.intValue());
        long total = query.count();
        List<ProcessVo> resultList = new ArrayList<>();
        for(HistoricTaskInstance item : historicTasks){
            String processInstanceId = item.getProcessInstanceId();
            Process process = getOne(new LambdaQueryWrapper<Process>()
                    .eq(Process::getProcessInstanceId, processInstanceId));
            ProcessVo processVo = new ProcessVo();
            BeanUtils.copyProperties(process, processVo);
            resultList.add(processVo);
        }
        Page<ProcessVo> voPage = new Page<>(page,limit,total);
        voPage.setRecords(resultList);
        return voPage;
    }

    /**
     * 查询当前用户已发起的流程信息
     * @param pageParam
     * @return
     */
    @Override
    public IPage<ProcessVo> findStarted(Page<ProcessVo> pageParam) {
        ProcessQueryVo queryVo = new ProcessQueryVo();
        queryVo.setUserId(LoginUserInfoHelper.getUserId());
        return this.baseMapper.selectPage(pageParam, queryVo, null);
    }
}
